package pl.elka.pszt.model;

import java.io.File;
import java.util.List;

import pl.elka.pszt.controller.Controller;
import pl.elka.pszt.controller.event.view.ShowInformationEvent;
import pl.elka.pszt.controller.event.view.ShowTextInFieldEvent;
import pl.elka.pszt.controller.event.view.ShowWarningEvent;
import pl.elka.pszt.model.argument.ArgumentHelper;
import pl.elka.pszt.model.literal.LiteralHelper;
import pl.elka.pszt.model.parser.Parser;
import pl.elka.pszt.model.solver.DefaultSolver;
import pl.elka.pszt.model.solver.Solver;
import pl.elka.pszt.utils.Log;
import pl.elka.pszt.utils.TextUtils;

/**
 * Model przechowuje helpery literalow i argumentow, oraz baze wiedzy. Umozliwia
 * tez obsluge solvera.
 * 
 * @author Andrzej Makarewicz
 * 
 */
public class Model {
	
	private static final String TAG = Model.class.getSimpleName();
	
	private final LiteralHelper literalHelper = new LiteralHelper();
	private final ArgumentHelper argumentHelper = new ArgumentHelper();
	private final Knowledge knowledge = new Knowledge(literalHelper, argumentHelper);
	
	private final Solver solver;
	
	private final Controller controller;
	
	public Model(Controller controller) {
		if (controller == null) throw new NullPointerException();
		this.controller = controller;
		
		solver = new DefaultSolver(this);
		solver.setKnowledge(knowledge);
	}
	
	/**
	 * Laduje wiedze z pliku. Czysci aktualnie zaladowana.
	 * 
	 * @param file
	 */
	public void loadKnowledge(File file) {
		if (file == null || !file.exists()) {
			controller.onEvent(new ShowWarningEvent("Nie udało się wczytać pliku."));
			return;
		}
		
		Log.o(TAG, Log.getCurrentMethodName() + " " + file.getPath());
		clear();
		knowledge.loadFromFile(file);
	}
	
	/**
	 * Zadaje pytanie do bazy wiedzy poprzez solver. Odpowiedz przyjdzie
	 * asynchronicznie.
	 * 
	 * @param question
	 */
	public void askQuestion(String question) {
		if (TextUtils.isEmpty(question)) {
			controller.onEvent(new ShowWarningEvent("Zapytanie nie może być puste."));
			return;
		}
		
		Log.o(TAG, Log.getCurrentMethodName() + " " + question);
		Fact fact = Parser.parseFact(question, argumentHelper, literalHelper);
		if (fact == null) {
			controller.onEvent(new ShowWarningEvent("Niezrozumiałe zapytanie."));
			return;
		}
		
		solver.setQuestion(new Assigned(fact));
		solver.solve();
	}
	
	/**
	 * Ustawia liczbe watkow w solverze.
	 * @param threadsNumber
	 */
	public void setThreads(int threadsNumber) {
		solver.setThreadsNumber(threadsNumber);
	}
	
	/**
	 * Wykonuje akcje po zakonczonym wnioskowaniu, czyli wypisuje je do gui.
	 * @param success
	 * @param k
	 * @param time
	 */
	public void onReasoningFinished(boolean success, List<KnowledgeElement> k, long time) {
		Log.o(TAG, "found: " + success + " time: " + time + " k: " + k.toString());
		
		controller.onEvent(new ShowTextInFieldEvent(k.toString()));
		
		String msg = String.format("Reasoning finished in: %d.\nAnswer to asked question is: %s", time, success);
		
		controller.onEvent(new ShowInformationEvent(msg));
	}
	
	/**
	 * Czysci helpery argumentow i literalow oraz baze wiedzy.
	 */
	public void clear() {
		literalHelper.clear();
		argumentHelper.clear();
		knowledge.clear();
	}
	
	/**
	 * Zamyka model.
	 */
	public void close() {
		solver.close();
	}
	
	/*********************************** TEST ***********************************/
	/**
	 * Funkcja testujaca.
	 */
	@Deprecated
	public void test() {
		Log.o(TAG, Log.getCurrentMethodName());
		
		// knowledge.loadFromResource("test.txt");
		// Fact question = Parser.parseFact("KOCHA(Babacki,Wspin)",
		// argumentHelper, literalHelper);
		
		knowledge.loadFromResource("simpleKnowledge.txt");
		// solver.setKnowledge(knowledge);
		
		Fact question = Parser.parseFact("P1001(ANIA,Const)", argumentHelper, literalHelper);
		solver.setQuestion(new Assigned(question));
		
		solver.setThreadsNumber(3);
		
		solver.solve();
	}
}
